package Model;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Observable;

public class Game extends Observable {
    // ATTRIBUTE

    private ArrayList<Clan> clanList;
    private Board board;
    private HashMap<Integer, Animal> animalList;
    private int numberTotalOfBeings;
    private boolean run = true;

    // CONSTRUCTOR
    public Game() {
        ArrayList<Clan> clanList = new ArrayList<Clan>();
        HashMap<Integer, Animal> animalList = new HashMap<Integer, Animal>();

        numberTotalOfBeings = 0;
    }

    public Game(Board board) {
        ArrayList<Clan> clanList = new ArrayList<Clan>();
        HashMap<Integer, Animal> animalList = new HashMap<Integer, Animal>();

        this.board = board;

        numberTotalOfBeings = 0;
    }

    // OWN FUNCTIONS
    public void start() {
        int effectiveSpeed = Parameter.getEffectiveSpeed();

        for (int i = 1; i <= effectiveSpeed; i++) {
            for (int j = 1; j <= numberTotalOfBeings; j++) {
                Being beingJ = this.getBeingFromId(j);

                ArrayList<Square> neighboursSquareOfj = board.getNeighbours(beingJ.getVisionRadius(), beingJ.getLocation());
                ArrayList<Square> handToHandSquareOfj = board.getNeighbours(1, beingJ.getLocation());

                if (beingJ.getSpeed() >= i) {
                    beingJ.decideAction(neighboursSquareOfj, handToHandSquareOfj);
                }
            }
        }
    }

    public Being getBeingFromId(int id) {
        Being res = null;
        // id in the animals ?
        if (this.animalList.containsKey(id)) {
            res = this.animalList.get(id);
        }

        // id in the clanlist villagers?
        for (int i = 0; i < this.clanList.size(); i++) {
            if (this.clanList.get(i).getVillagerList().containsKey(id)) {
                res = this.clanList.get(i).getVillagerList().get(id);
                break;
            }
        }
        return res;
    }

    public void createClan(String clanName, int qgX, int qgY, int nbHunters, int nbBuilders, int nbStoneminer, int nbLumberjack, int nbGoldminer, int nbFisherman) {
        Location qgLocation = new Location(qgX, qgY);
        Square squareQG = this.getBoard().getSquareFromLocation(qgLocation);

        Clan clan = new Clan(clanName, squareQG, board);
        Location freeLocation;
        for (int i = 0; i < nbHunters; i++) {
            freeLocation = board.getFreeLocation(qgLocation);
            Villager v = new Villager(numberTotalOfBeings, Parameter.getVillagerSpeed(), freeLocation, Parameter.getVillagerVisionRadius(), Activity.Hunter, clan);
            board.getSquareFromLocation(freeLocation).setBeingReference(v);
            clan.addVillager(v);
            numberTotalOfBeings++;
        }

        for (int i = 0; i < nbBuilders; i++) {
            freeLocation = board.getFreeLocation(qgLocation);
            Villager v = new Villager(numberTotalOfBeings, Parameter.getVillagerSpeed(), freeLocation, Parameter.getVillagerVisionRadius(), Activity.Builder, clan);
            board.getSquareFromLocation(freeLocation).setBeingReference(v);
            clan.addVillager(v);
            numberTotalOfBeings++;
        }

        for (int i = 0; i < nbStoneminer; i++) {
            freeLocation = board.getFreeLocation(qgLocation);
            Villager v = new Villager(numberTotalOfBeings, Parameter.getVillagerSpeed(), freeLocation, Parameter.getVillagerVisionRadius(), Activity.Stoneminer, clan);
            board.getSquareFromLocation(freeLocation).setBeingReference(v);
            clan.addVillager(v);
            numberTotalOfBeings++;
        }

        for (int i = 0; i < nbLumberjack; i++) {
            freeLocation = board.getFreeLocation(qgLocation);
            Villager v = new Villager(numberTotalOfBeings, Parameter.getVillagerSpeed(), freeLocation, Parameter.getVillagerVisionRadius(), Activity.Lumberjack, clan);
            board.getSquareFromLocation(freeLocation).setBeingReference(v);
            clan.addVillager(v);
            numberTotalOfBeings++;
        }

        for (int i = 0; i < nbGoldminer; i++) {
            freeLocation = board.getFreeLocation(qgLocation);
            Villager v = new Villager(numberTotalOfBeings, Parameter.getVillagerSpeed(), freeLocation, Parameter.getVillagerVisionRadius(), Activity.Goldminer, clan);
            board.getSquareFromLocation(freeLocation).setBeingReference(v);
            clan.addVillager(v);
            numberTotalOfBeings++;
        }

        for (int i = 0; i < nbFisherman; i++) {
            freeLocation = board.getFreeLocation(qgLocation);
            Villager v = new Villager(numberTotalOfBeings, Parameter.getVillagerSpeed(), freeLocation, Parameter.getVillagerVisionRadius(), Activity.Fisherman, clan);
            board.getSquareFromLocation(freeLocation).setBeingReference(v);
            clan.addVillager(v);
            numberTotalOfBeings++;
        }

        this.clanList.add(clan);

    }

    public void createAnimal(AnimalType animalType, int quantity) {
        if (quantity > 0) {
            for (int i = 0; i < quantity; i++) {
                Location randomLocation = board.getRandomFreeLocation();
                //Et si plus de randomLocation ?
                Animal newAnimal = new Animal(numberTotalOfBeings, Parameter.getAnimalSpeed(), randomLocation, Parameter.getAnimalVisionRadius(), animalType);
                board.getSquareFromLocation(randomLocation).setBeingReference(newAnimal);
                numberTotalOfBeings++;

                this.animalList.put(newAnimal.getId(), newAnimal);
            }
        }
    }

    public void startDebugMode() {

        int nbSteps = 1000000;
        System.out.println("Number of Beings = " + numberTotalOfBeings + " Number of steps : " + nbSteps);

        ArrayList<Square> neighboursSquareOfj = new ArrayList<Square>();
        ArrayList<Square> handToHandSquareOfj = new ArrayList<Square>();

        while (run) {
            Statistics.setNbSteps(Statistics.getNbSteps() + 1);
            //System.out.println("+++++++++++++++++ Step Number : " + Statistics.getNbSteps() + " ++++++++++++++++++++");
            for (int i = 0; i < this.numberTotalOfBeings; i++) {

                Being beingJ = this.getBeingFromId(i);

                if (beingJ.isAlive()) {
                    //Verification
                    Square s = board.getSquareFromLocation(beingJ.getLocation());
                    if (s.getBeingReference() == null) {
                        System.out.println("@@@@@@@@@@@@@@@@@@@@ Incoherence with board : no being @@@@@@@@@@@@@@@@");
                        System.out.println("@@@@@@@@@@@@@@@@@@@@ being id : " + beingJ.getId() + " @@@@@@@@@@@@@@@@");
                    } else if (s.getBeingReference().getId() != beingJ.getId()) {
                        System.out.println("@@@@@@@@@@@@@@@@@@@@ Incoherence with board : wrong being @@@@@@@@@@@@@@@@");
                        System.out.println("@@@@@ being id : " + beingJ.getId() + " board being id : " + s.getBeingReference().getId() + " @@@@@@@@@@@@@@@@");
                        System.out.println("*** Info being ***");
                        beingJ.displayInfoInTerminal();
                        System.out.println("\n *** Info being in board ***");
                        s.getBeingReference().displayInfoInTerminal();
                    }
                    //Fin Verification

                    Action action = null;

                    neighboursSquareOfj = board.getNeighbours(beingJ.getVisionRadius(), beingJ.getLocation());
                    handToHandSquareOfj = board.getNeighbours(1, beingJ.getLocation());

                    action = beingJ.decideAction(neighboursSquareOfj, handToHandSquareOfj);

                    boolean debug = false;
                    //Verification
                    if (debug && action != null) {
                        System.out.println("\n__________________________________");
                        System.out.println("### Info being : " + beingJ.getId() + " ###");
                        beingJ.displayInfoInTerminal();
                        System.out.println("### Info action choosen ###");
                        action.displayInTerminal();
                    }
                    //Fin Verification

                    doAction(action, beingJ);

                    //Verification
                    if (debug && action != null && action.getDecision() == 2) {
                        System.out.println("### Info being after action : " + beingJ.getId() + " ###");
                        beingJ.displayInfoInTerminal();
                        System.out.println("__________________________________");
                    }
                    //Fin Verification
                }

                try {
                    Thread.sleep(Parameter.getGameDelay());
                } catch (InterruptedException e) {
                }
            }

            setChanged();
            notifyObservers();
            if (Parameter.isGameHold()) {
                //TO DO
            }
        }
    }

    public void initTestMode() {
        //lumberjack 1
        Location freeLocation = board.getFreeLocation(new Location(15, 15));
        Villager v = new Villager(numberTotalOfBeings, Parameter.getVillagerSpeed(), freeLocation, Parameter.getVillagerVisionRadius(), Activity.Stoneminer, this.clanList.get(0));
        board.getSquareFromLocation(freeLocation).setBeingReference(v);
        this.clanList.get(0).addVillager(v);

        this.clanList.get(0).getSquareQG().getLocation().displayInTerminal();
        numberTotalOfBeings++;
    }

    public void initDebugMode() {
        // initialization of the board
        this.board = new Board(Parameter.getWidthBoard(), Parameter.getHeightBoard());

        // initialisation of clans
        // HUNTER 1
        clanList = new ArrayList<Clan>();
        Location loc = new Location(0, 0);
        Clan clan1 = new Clan("clan1", board.getSquareFromLocation(loc), board);

        Villager villager = new Villager(numberTotalOfBeings, 2, new Location(3, 3), 2, Activity.Hunter, clan1);
        numberTotalOfBeings++;
        board.getSquareFromLocation(new Location(3, 3)).setBeingReference(villager);
        clan1.addVillager(villager);
        clanList.add(clan1);

        // initisalization of the animals.
        this.animalList = new HashMap<Integer, Animal>();


        // ANIMAL 1
        Animal animal1 = new Animal(numberTotalOfBeings, 2, new Location(1, 2), 2, AnimalType.Doe);
        numberTotalOfBeings++;
        board.getSquareFromLocation(new Location(1, 2)).setBeingReference(animal1);
        this.animalList.put(animal1.getId(), animal1);

        // ANIMAL 2
        Animal animal2 = new Animal(numberTotalOfBeings, 2, new Location(1, 3), 2, AnimalType.Lion);
        numberTotalOfBeings++;
        board.getSquareFromLocation(new Location(1, 3)).setBeingReference(animal2);
        this.animalList.put(animal2.getId(), animal2);

        // ANIMAL 3
        Animal animal3 = new Animal(numberTotalOfBeings, 2, new Location(3, 2), 2, AnimalType.Doe);
        numberTotalOfBeings++;
        board.getSquareFromLocation(new Location(3, 2)).setBeingReference(animal3);
        this.animalList.put(animal3.getId(), animal3);


    }

    public void refreshBoard(boolean resetBoard) {

        if (resetBoard) {
            board.resetAllBeingsReference();
        }

        for (int k = 0; k < numberTotalOfBeings; k++) {
            Being being = this.getBeingFromId(k);
            if (being != null) {
                board.getSquareFromLocation(being.getLocation()).setBeingReference(being);
            } else {
                System.out.println("------------------ Being number " + k + " is null !? ------------------ in refresh");
            }
        }
    }

    public void doAction(Action action, Being being) {
        if (action == null) {
            //System.out.println("Action of being number " + being.getId() + " is null --in doAction");
            return;
        }

        if (!being.isAlive()) {
            //System.out.println("being number " + being.getId() + " is dead --in doAction");
            return;
        }

        Location oldPosition = new Location(being.getLocation().getXPosition(), being.getLocation().getYPosition());
        boolean majSuccess = false;

        switch (action.getDecision()) {
            case 0: // Escape From
                being.moveInOppositeDirection(action.getDirectDirection());
                majSuccess = this.board.updatePosition(oldPosition, being);
                break;
            case 1: // Move POI to
                being.moveInDirection(action.getDirectDirection());
                majSuccess = this.board.updatePosition(oldPosition, being);
                break;
            case 2: // Do own action
                being.doSpecificAction(action, this.board.getSquareFromLocation(being.getLocation().getLocationInDirectDirection(action.getDirectDirection())));
                this.board.applyAction(action.getDirectDirection(), being);
                majSuccess = true;
                break;
            case 3:
                ///System.out.println("Preparing to move");
                being.moveInDirection(action.getDirectDirection());
                majSuccess = this.board.updatePosition(oldPosition, being);
                /*if (majSuccess) {
                 System.out.println("Move successfull");
                 } else {
                 System.out.println("Failed to move");
                 System.out.println("Direction " + action.getDirectDirection().name());
                 System.out.println("Being position : ");
                 being.getLocation().displayInTerminal();
                 }*/
                break;
            default:
                majSuccess = true;
                break;
        }

        if (!majSuccess) {
            System.out.println("Maj position problem | Move cancelled | Being ID : " + being.getId());
            /*   System.out.println("************************* MAJ POSITION PROBLEM ****************************");
             System.out.println("Wanted Position :");
             being.getLocation().displayInTerminal();
             System.out.println("\nOld Position :");
             oldPosition.displayInTerminal();
             being.setLocation(oldPosition);
             being.setLocation(oldPosition);
             System.out.println("\nPosition after cancelling transaction ");
             being.getLocation().displayInTerminal();
             System.out.println("\n************************* FIN MAJ POSITION PROBLEM ****************************");*/
            being.setLocation(oldPosition);
            //this.run = false;
        }
    }

    // GETTER - SETTER
    public ArrayList<Clan> getClanList() {
        return this.clanList;
    }

    public Board getBoard() {
        return this.board;
    }

    public HashMap<Integer, Animal> getAnimalList() {
        return this.animalList;
    }

    public void setClanList(ArrayList<Clan> clanList) {
        this.clanList = clanList;
    }

    public void setBoard(Board board) {
        this.board = board;
    }

    public void setAnimalList(HashMap<Integer, Animal> animalList) {
        this.animalList = animalList;
    }

    public int getNumberTotalOfBeings() {
        return numberTotalOfBeings;
    }

    public void setNumberTotalOfBeings(int numberTotalOfBeings) {
        this.numberTotalOfBeings = numberTotalOfBeings;
    }

    public void createBuilding(Location location) {
        this.board.getSquareFromLocation(location).setBuildingReference(new Building(location));
    }
}
